#ifndef __VM_H
#define __VM_H 1

#include <vector>
#include <string>
#include <stdint.h>
#include <stdio.h>

#include <instruction.h>

#include <common.h>


using float32_t = float;


class VM {
  private:
    char *_base;
    size_t _size;
    uint32_t reg[32];
    float32_t freg[32];
  public:
    int32_t * const s = (int32_t *)&reg[0];
    uint32_t* const u = &reg[0];
    uint32_t &zero = reg[0];
    uint32_t &at = reg[1];
    uint32_t &v0 = reg[2];
    uint32_t &v1 = reg[3];
    uint32_t &a0 = reg[4];
    uint32_t &a1 = reg[5];
    uint32_t &a2 = reg[6];
    uint32_t &a3 = reg[7];
    uint32_t &t0 = reg[8];
    uint32_t &t1 = reg[9];
    uint32_t &t2 = reg[10];
    uint32_t &t3 = reg[11];
    uint32_t &t4 = reg[12];
    uint32_t &t5 = reg[13];
    uint32_t &t6 = reg[14];
    uint32_t &t7 = reg[15];
    uint32_t &s0 = reg[16];
    uint32_t &s1 = reg[17];
    uint32_t &s2 = reg[18];
    uint32_t &s3 = reg[19];
    uint32_t &s4 = reg[20];
    uint32_t &s5 = reg[21];
    uint32_t &s6 = reg[22];
    uint32_t &s7 = reg[23];
    uint32_t &t8 = reg[24];
    uint32_t &t9 = reg[25];
    uint32_t &k0 = reg[26];
    uint32_t &k1 = reg[27];
    uint32_t &gp = reg[28];
    uint32_t &sp = reg[29];
    uint32_t &fp = reg[30];
    uint32_t &ra = reg[31];
    uint32_t hi, lo;
    int pc;
  public:
    VM(void *base, size_t size);
    void run();

    void *addr(int off) { return _base + off;}

    Instruction fetch() {
      Instruction i{*(uint32_t *)(_base + pc)};
      pc += sizeof i;
      return i;
    }

    std::vector<std::string> dump();

    ~VM();
};

std::string dump(Instruction i);

#endif // __VM_H

